package com.wk.springboot.httpUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Map;
import com.google.common.base.Optional;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.conn.UnsupportedSchemeException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Classname DrspHttpClient
 * @Description httpClient处理类
 * @Date 2020/8/7 11:15
 * @Author by weikai
 */
public class DrspHttpClient implements IHttpClient{

  private static Logger LOG = LoggerFactory.getLogger(DrspHttpClient.class);

  private CloseableHttpClient httpClient;

  public DrspHttpClient(CloseableHttpClient httpClient) {
    this.httpClient = httpClient;
  }

  @Override
  public String post(String url, String contentType, String body, Map<String, String> headerMap) throws DrspHttpException {
    String result = this.postString(url, contentType, body, headerMap);
    return StringUtils.trimToEmpty(result);
  }

  @Override
  public String post(String url, String body, Map<String, String> headerMap) throws DrspHttpException {
    return this.post(url, "text/plain", body, headerMap);
  }

  @SuppressWarnings("unchecked")
  @Override
  public String post(String url, String contentType, String body) throws DrspHttpException {
    String result = this.post(url, contentType, body, MapUtils.EMPTY_MAP);
    return StringUtils.trimToEmpty(result);
  }

  /**
   * 释放连接
   * @param requestBase
   */
  public void releaseConnection(HttpRequestBase requestBase) {
    requestBase.releaseConnection();
  }

  /**
   * 关闭响应
   * @param response
   * @throws DrspHttpException
   */
  public void closeResponse(CloseableHttpResponse response) throws DrspHttpException {
    if (response != null) {
      try {
        response.close();
      } catch (IOException e) {
        String errorMessage = "未知异常";
        LOG.error(errorMessage);
        throw new DrspHttpException(errorMessage, e);
      }
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  public String get(String url, String contentType, Map<String, String> urlParams) throws DrspHttpException {
    String result = this.get(url, contentType, urlParams, MapUtils.EMPTY_MAP);
    return StringUtils.trimToEmpty(result);
  }

  /**
   * 验证http响 应状态码
   * @param url
   * @param response
   * @throws DrspHttpException
   */
  private void checkResponseCode(String url, DrspHttpResponse response) throws DrspHttpException {
    int statusCode = response.getStatusCode();
    if (statusCode != HttpStatus.SC_OK) {
      LOG.error("HTTP调用失败,URL={}", url);
      throw new DrspHttpException(statusCode, "HTTP调用失败");
    }

    LOG.debug("HTTP调用成功,URL={}", url);
  }

  @SuppressWarnings("unchecked")
  @Override
  public String get(String url, String contentType) throws DrspHttpException {
    return this.get(url, contentType, MapUtils.EMPTY_MAP);
  }

  /**
   * 将部分http 异常转为 DrspHttpException
   * @param e 异常
   * @return
   * @throws DrspHttpException
   */
  public DrspHttpException parseDrspHttpException(Exception e) throws DrspHttpException {
    DrspHttpException exception = null;
    String errorMessage = "";
    if (e instanceof UnknownHostException) {
      errorMessage = "HTTP调用失败,域名解析出错，请检查URL";
      exception = new DrspHttpException(errorMessage, e);
    } else if (e instanceof HttpHostConnectException) {
      errorMessage = "HTTP调用失败,ip或端口配错了,请检查URL";
      exception = new DrspHttpException(errorMessage, e);
    } else if (e instanceof SocketTimeoutException) {
      errorMessage = "指定时间内未返回结果";
      exception = new DrspHttpException(errorMessage, e);
    } else if (e instanceof ConnectionPoolTimeoutException) {
      errorMessage = "从连接池中时间内获取不到连接";
      exception = new DrspHttpException(errorMessage, e);
    } else if (e instanceof UnsupportedSchemeException) {
      errorMessage = "URL不符合http协议";
      exception = new DrspHttpException(errorMessage, e);
    } else {
      errorMessage = "未知异常";
      exception = new DrspHttpException(errorMessage, e);
    }

    LOG.error(errorMessage);
    throw exception;
  }

  @Override
  public String postForm(String url, Map<String, String> formParams, Map<String, String> headerMap) throws DrspHttpException {

    DrspHttpPost httpPost = null;
    DrspHttpResponse response = null;

    try {

      httpPost = new DrspHttpPost(this.httpClient);

      httpPost.setUrl(url).setHeader(headerMap);
      httpPost.setFormParams(formParams);

      response = httpPost.execute();

      checkResponseCode(url, response);

      return response.getText();

    } catch (Exception e) {
      throw new DrspHttpException(parseDrspHttpException(e));
    } finally {
      httpPost.releaseConnection();
      response.closeResponse();
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  public String postForm(String url, Map<String, String> formParams) throws DrspHttpException {
    return this.postForm(url, formParams, MapUtils.EMPTY_MAP);
  }

  @Override
  public String post(String url, InputStream stream, Map<String, String> headerMap) throws DrspHttpException {
    DrspHttpPost httpPost = null;
    DrspHttpResponse response = null;
    try {
      httpPost = new DrspHttpPost(this.httpClient);
      httpPost.setUrl(url).setHeader(headerMap);
      httpPost.setBody(stream);

      response = httpPost.execute();

      checkResponseCode(url, response);

      return response.getText();

    } catch (Exception e) {
      throw new DrspHttpException(parseDrspHttpException(e));
    } finally {
      httpPost.releaseConnection();
      response.closeResponse();
      IOUtils.closeQuietly(stream);
    }
  }

  @Override
  public String post(String url, MultipartEntityBuilderWraper entityBuilder, Map<String, String> headerMap) throws DrspHttpException {
    DrspHttpPost httpPost = null;
    DrspHttpResponse response = null;
    try {
      httpPost = new DrspHttpPost(this.httpClient);
      httpPost.setUrl(url).setHeader(headerMap);
      httpPost.setMultiPartBuilder(entityBuilder);

      response = httpPost.execute();

      checkResponseCode(url, response);

      return response.getText();

    } catch (Exception e) {
      throw new DrspHttpException(parseDrspHttpException(e));
    } finally {
      httpPost.releaseConnection();
      response.closeResponse();
    }

  }

  /**
   * post 文本
   * @param url
   * @param contentType
   * @param body
   * @param headerMap
   * @return
   * @throws DrspHttpException
   */
  private String postString(String url, String contentType, String body, Map<String, String> headerMap) throws DrspHttpException {
    DrspHttpPost httpPost = null;
    DrspHttpResponse response = null;
    try {
      httpPost = new DrspHttpPost(this.httpClient);

      httpPost.setUrl(url).setHeader(headerMap);
      httpPost.setContentType(contentType);
      httpPost.setBody(body);

      response = httpPost.execute();

      checkResponseCode(url, response);
      return response.getText();

    } catch (Exception e) {
      throw new DrspHttpException(parseDrspHttpException(e));
    } finally {
      close(httpPost, response);
    }
  }

  private void close(DrspHttpMethod httpMethod, DrspHttpResponse response) throws DrspHttpException {
    if (httpMethod != null) {
      httpMethod.releaseConnection();
    }
    if (response != null) {
      response.closeResponse();
    }
  }

  @Override
  public boolean getStream(String url, String contentType, Map<String, String> urlParams, IStreamExecutor executor) throws DrspHttpException {
    DrspHttpGet getMethod = null;
    DrspHttpResponse response = null;
    Optional<InputStream> result = null;
    try {
      getMethod = new DrspHttpGet(this.httpClient);
      getMethod.setUrl(url).setUrlParams(urlParams);
      getMethod.setContentType(contentType);

      response = getMethod.execute();

      checkResponseCode(url, response);

      result = Optional.fromNullable(response.getStream());
      return executor.execute(result);

    } catch (Exception e) {
      throw new DrspHttpException(parseDrspHttpException(e));
    } finally {
      close(getMethod, response);
      IOUtils.closeQuietly(result.get());
    }
  }

  private String get(String url, String contentType, Map<String, String> urlParams, Map<String, String> headerMap) throws DrspHttpException {

    DrspHttpGet getMethod = null;
    DrspHttpResponse response = null;
    try {
      getMethod = new DrspHttpGet(this.httpClient);
      getMethod.setUrl(url).setUrlParams(urlParams);
      getMethod.setContentType(contentType).setHeader(headerMap);

      response = getMethod.execute();

      checkResponseCode(url, response);
      return response.getText();

    } catch (Exception e) {
      throw parseDrspHttpException(e);
    } finally {
      getMethod.releaseConnection();
      response.closeResponse();
    }

  }

  public CloseableHttpClient getHttpClient() {
    return httpClient;
  }

  @Override
  public void close() {
    try {
      this.httpClient.close();
    } catch (IOException e) {
      LOG.error("关闭httpclient是出错", e);
    }
  }
}
